/* * Author: Chris Seguin * * This software has been developed under the copyleft * rules of the GNU General Public License. Please * consult the GNU General Public License for more * details about use and distribution of this software. */ package org.acm.seguin.refactor.field; import org.acm.seguin.parser.ChildrenVisitor; import org.acm.seguin.parser.ast.ASTClassBody; import org.acm.seguin.parser.ast.ASTClassBodyDeclaration; import org.acm.seguin.parser.ast.ASTFieldDeclaration; import org.acm.seguin.parser.ast.ASTInterfaceBody; import org.acm.seguin.parser.ast.ASTInterfaceMemberDeclaration; import org.acm.seguin.parser.ast.ASTNestedClassDeclaration; import org.acm.seguin.parser.ast.ASTNestedInterfaceDeclaration; import org.acm.seguin.parser.ast.ASTVariableDeclarator; import org.acm.seguin.parser.ast.ASTVariableDeclaratorId; import org.acm.seguin.parser.ast.SimpleNode; /** * Visitor that traverses an AST and removes a specified field * *@author Chris Seguin */ public class RemoveFieldVisitor extends ChildrenVisitor { private String fieldName; private SimpleNode fieldDecl; /** * Constructor for RemoveFieldVisitor that specifies the field to remove * *@param field the name of the field */ public RemoveFieldVisitor(String field) { fieldName = field; fieldDecl = null; } /** * Gets the FieldDeclaration attribute of the RemoveFieldVisitor object * *@return The FieldDeclaration value */ public SimpleNode getFieldDeclaration() { return fieldDecl; } /** * Visit a class body * *@param node the class body node *@param data the data for the visitor *@return the field if it is found */ public Object visit(ASTClassBody node, Object data) { Object result = removeField(node); if (result == null) { result = super.visit(node, data); } return result; } /** * Visit an interface body * *@param node the interface body node *@param data data for the visitor *@return the field that was removed */ public Object visit(ASTInterfaceBody node, Object data) { Object result = removeField(node); if (result == null) { result = super.visit(node, data); } return result; } /** * Skip nested classes * *@param node the nested class *@param data the data for the visitor *@return the field if it is found */ public Object visit(ASTNestedClassDeclaration node, Object data) { return null; } /** * Skip nested interfaces * *@param node the nested interface *@param data the data for the visitor *@return the field if it is found */ public Object visit(ASTNestedInterfaceDeclaration node, Object data) { return null; } /** * Have we found the field declaration that we are going to move? * *@param next Description of Parameter *@return The Found value */ private boolean isFound(SimpleNode next) { if (!(next instanceof ASTFieldDeclaration)) { return false; } int loop = next.jjtGetNumChildren(); for (int ndx = 1; ndx < loop; ndx++) { if (checkDeclaration(next, ndx)) { return true; } } return false; } /** * Determines if we are visiting a node that has multiple fields declared in * a single statement * *@param field Description of Parameter *@return The Multiple value */ private boolean isMultiple(SimpleNode field) { return (field.jjtGetNumChildren() > 2); } /** * Remove the field, if it is the correct one. Return the field declaration * that was removed * *@param node The node we are considering removing the field from *@return The field declaration */ private Object removeField(SimpleNode node) { int loop = node.jjtGetNumChildren(); for (int ndx = 0; ndx < loop; ndx++) { SimpleNode next = (SimpleNode) node.jjtGetChild(ndx); SimpleNode possible = (SimpleNode) next.jjtGetChild(0); if (isFound(possible)) { if (isMultiple(possible)) { removeMultiple((ASTFieldDeclaration) possible, next instanceof ASTClassBodyDeclaration); } else { removeSingle(node, next, ndx); } return next; } } return null; } /** * Removes a field declaration where only a single variable was declared * *@param node the class or interface body node *@param next the container for the field declaration *@param ndx the index of the node to delete */ private void removeSingle(SimpleNode node, SimpleNode next, int ndx) { fieldDecl = next; node.jjtDeleteChild(ndx); } /** * Removes a field that is declared as one of many * *@param next the field declaration *@param isClass was this in a class or an interface */ private void removeMultiple(ASTFieldDeclaration next, boolean isClass) { if (isClass) { fieldDecl = new ASTClassBodyDeclaration(0); } else { fieldDecl = new ASTInterfaceMemberDeclaration(0); } // Create the field declaration ASTFieldDeclaration afd = new ASTFieldDeclaration(0); fieldDecl.jjtInsertChild(afd, 0); // Copy the type afd.jjtInsertChild(next.jjtGetChild(0), 0); // Find the variable and remove it from the old and add it to the new int loop = next.jjtGetNumChildren(); for (int ndx = 1; ndx < loop; ndx++) { if (checkDeclaration(next, ndx)) { afd.jjtInsertChild(next.jjtGetChild(ndx), 1); next.jjtDeleteChild(ndx); return; } } } /** * Checks a single variable declaration to see if it is the one we are * looking for * *@param next the field declaration that we are checking *@param index the index of the id that we are checking *@return true if we have found the field */ private boolean checkDeclaration(SimpleNode next, int index) { ASTVariableDeclarator decl = (ASTVariableDeclarator) next.jjtGetChild(index); ASTVariableDeclaratorId id = (ASTVariableDeclaratorId) decl.jjtGetChild(0); return (id.getName().equals(fieldName)); } }